<!DOCTYPE html>
<html>

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no"/>
<title>Arcade - temperatures</title>

<link rel="stylesheet" href="https://js.arcgis.com/3.19/esri/css/esri.css">
<link rel="stylesheet" href="https://js.arcgis.com/3.19/dijit/themes/claro/claro.css">
<script src="https://js.arcgis.com/3.19/"></script>

<style>
  html, body, #viewDiv {
    height: 100%;
    width: 100%;
    margin: 0;
    padding: 0;
  }
  #info{
    bottom: 0px;
    left: 0px;
    position: absolute;
    z-index: 99;
    opacity: 0.9;
    background-color: white;
    padding: 10px;
    max-width: 280px;
  }
</style>
  
<!--------------------->  
<!-- ARCADE SCRIPTS  -->
<!--------------------->
  
  
<!--WIND CHILL-->
<!--http://www.nws.noaa.gov/om/winter/windchill.shtml-->
  
<script type="text/plain" id="wind-chill">
  // if no temperature or wind speed is available don't render feature
  if(IsEmpty($feature.TEMP) || IsEmpty($feature.WIND_SPEED)){
    return null;
  }
  var t = $feature.TEMP;
  // convert speed from kph to mph
  var ws = $feature.WIND_SPEED * 0.621371;

  var wc = 35.74 + (0.6215 * t) - (35.75 * Pow(ws, 0.16)) + (0.4275 * t * Pow(ws, 0.16))

  // Only for temps below 50F and WS > 3mph
  // wind chill must be lower than the temperature
  return IIF(t <= 50 && ws >= 3 && wc < t, wc, null);
</script>
  
<!--HEAT INDEX-->
<!--http://www.nws.noaa.gov/os/heat/heat_index.shtml  -->
  
<script type="text/plain" id="heat-index">
  // if no temperature or relative humidity is available don't render feature
  if(IsEmpty($feature.TEMP) || IsEmpty($feature.R_HUMIDITY)){
    return null;
  }

  var c1 = -42.379;
  var c2 = 2.04901523;
  var c3 = 10.14333127;
  var c4 = -0.22475541;
  var c5 = -0.00683783;
  var c6 = -0.05481717;
  var c7 = 0.00122874;
  var c8 = 0.00085282;
  var c9 = -0.00000199;

  var t = $feature.TEMP;
  var r = $feature.R_HUMIDITY;

  var hi = c1 + (c2 * t) + (c3 * r) 
    + (c4 * t * r) + (c5 * t * t) 
    + (c6 * r * r) + (c7 * t * t * r) 
    + (c8 * t * r * r) + (c9 * t * t * r * r);

  // Only for temps above 80F and RH above 40%
  return IIF(t >= 80 && r >= 40, hi, null);
</script>
  
<!--APPARENT TEMPERATURE (HOW IT FEELS)-->
<!--https://graphical.weather.gov/definitions/defineApparentT.html-->
  
<script type="text/plain" id="apparent-temp">
  // if no temp, rh, or ws is available don't render feature
  if(  IsEmpty($feature.TEMP) 
    || IsEmpty($feature.R_HUMIDITY)
    || IsEmpty($feature.WIND_SPEED)
  ){
    return null;
  }

  var t = $feature.TEMP;
  var ws = $feature.WIND_SPEED * 0.621371;  // convert to mph
  var r = $feature.R_HUMIDITY;

  // WIND CHILL

  var wc = 35.74 + (0.6215 * t) 
    - (35.75 * Pow(ws, 0.16)) 
    + (0.4275 * t * Pow(ws, 0.16));

  // HEAT INDEX

  var c1 = -42.379;
  var c2 = 2.04901523;
  var c3 = 10.14333127;
  var c4 = -0.22475541;
  var c5 = -0.00683783;
  var c6 = -0.05481717;
  var c7 = 0.00122874;
  var c8 = 0.00085282;
  var c9 = -0.00000199;

  var hi = c1 + (c2 * t) + (c3 * r) 
    + (c4 * t * r) + (c5 * t * t) 
    + (c6 * r * r) + (c7 * t * t * r) 
    + (c8 * t * r * r) + (c9 * t * t * r * r);

  // apparent temperature
  var apTemp = WHEN(  
      // Only for temps below 50F and WS > 3mph
      // wind chill must be lower than the temperature
    t <= 50 && ws >= 3 && wc < t, wc,
      // Only for temps above 80F and RH above 40%
    t >= 80 && r >= 40, hi, 
      // all other points will display air temperature
    t
  );
  return apTemp;
</script>
  
<!--DIFFERENCE BETWEEN APPARENT (FELT) TEMPERATURE AND MEASURED AIR TEMPERATURE-->
  
<script type="text/plain" id="felt-temp-diff">
  // if no temp, rh, or ws is available don't render feature
  if(  IsEmpty($feature.TEMP) 
    || IsEmpty($feature.R_HUMIDITY)
    || IsEmpty($feature.WIND_SPEED)
  ){
    return null;
  }

  var t = $feature.TEMP;
  var ws = $feature.WIND_SPEED * 0.621371;
  var r = $feature.R_HUMIDITY;

  // WIND CHILL

  var wc = 35.74 + (0.6215 * t) 
    - (35.75 * Pow(ws, 0.16)) 
    + (0.4275 * t * Pow(ws, 0.16));

  // HEAT INDEX

  var c1 = -42.379;
  var c2 = 2.04901523;
  var c3 = 10.14333127;
  var c4 = -0.22475541;
  var c5 = -0.00683783;
  var c6 = -0.05481717;
  var c7 = 0.00122874;
  var c8 = 0.00085282;
  var c9 = -0.00000199;

  var hi = c1 + (c2 * t) + (c3 * r) 
    + (c4 * t * r) + (c5 * t * t) 
    + (c6 * r * r) + (c7 * t * t * r) 
    + (c8 * t * r * r) + (c9 * t * t * r * r);

  var tempDiff = WHEN(  
      // Only for temps below 50F and WS > 3mph
      // wind chill must be lower than the temperature
    t <= 50 && ws >= 3 && wc < t, wc - t,
      // Only for temps above 80F and RH above 40%
    t >= 80 && r >= 40, hi - t, 
      0
  );
  
  return tempDiff;
</script>  
  
<!--DIFFERENCE BETWEEN APPARENT (FELT) TEMPERATURE AND MEASURED AIR TEMPERATURE-->
<!--same as script above except the result is returned as an absolute value-->
  
<script type="text/plain" id="felt-temp-diff-abs">
  // if no temp, rh, or ws is available don't render feature
  if(  IsEmpty($feature.TEMP) 
    || IsEmpty($feature.R_HUMIDITY)
    || IsEmpty($feature.WIND_SPEED)
  ){
    return null;
  }

  var t = $feature.TEMP;
  var ws = $feature.WIND_SPEED * 0.621371;
  var r = $feature.R_HUMIDITY;

  // WIND CHILL

  var wc = 35.74 + (0.6215 * t) 
    - (35.75 * Pow(ws, 0.16)) 
    + (0.4275 * t * Pow(ws, 0.16));

  // HEAT INDEX

  var c1 = -42.379;
  var c2 = 2.04901523;
  var c3 = 10.14333127;
  var c4 = -0.22475541;
  var c5 = -0.00683783;
  var c6 = -0.05481717;
  var c7 = 0.00122874;
  var c8 = 0.00085282;
  var c9 = -0.00000199;

  var hi = c1 + (c2 * t) + (c3 * r) 
    + (c4 * t * r) + (c5 * t * t) 
    + (c6 * r * r) + (c7 * t * t * r) 
    + (c8 * t * r * r) + (c9 * t * t * r * r);

  var tempDiff = WHEN(  
      // Only for temps below 50F and WS > 3mph
      // wind chill must be lower than the temperature
    t <= 50 && ws >= 3 && wc < t, wc - t,
      // Only for temps above 80F and RH above 40%
    t >= 80 && r >= 40, hi - t, 
      0
  );
  
  return Abs(tempDiff);
</script>  
  
<!--
--UP ARROW INDICATES TEMP IS HOTTER DUE TO HUMIDITY
--DOWN ARROW INDICATES TEMP IS COOLER DUE TO WIND CHILL
--SIDE ARROW INDICATES AIR TEMP REFLECTS REALITY
-->
  
<script type="text/plain" id="arrow-direction">
  // if no temp, rh, or ws is available don't render feature
  if(  IsEmpty($feature.TEMP) 
    || IsEmpty($feature.R_HUMIDITY)
    || IsEmpty($feature.WIND_SPEED)
  ){
    return null;
  }

  var t = $feature.TEMP;
  var ws = $feature.WIND_SPEED * 0.621371;
  var r = $feature.R_HUMIDITY;

  var rotation = WHEN(  
      // if wind chill is used then point arrow
      // down 180 degrees
    t <= 50 && ws >= 3, 0,
      // if heat index is used, then point arrow
      // upward 0 degrees
    t >= 80 && r >= 40, 180, 
      // otherwise point it 90 degrees to the right
    270
  );
  
  return rotation;
</script>    

<script>
  require([
    "esri/map",
    "esri/layers/FeatureLayer",
    "esri/renderers/SimpleRenderer",
    "esri/symbols/SimpleMarkerSymbol",
    "esri/symbols/SimpleLineSymbol",
    "esri/Color",
    "esri/dijit/Legend",
    "esri/dijit/PopupTemplate",
    "dojo/on",
    "dojo/domReady!"
  ], function(Map, FeatureLayer, 
    SimpleRenderer, SimpleMarkerSymbol, SimpleLineSymbol, Color, 
    Legend, PopupTemplate, on
  ) {
    
    // FeatureLayer options
    
    var serviceUrl = "https://services.arcgis.com/V6ZHFr6zdgNZuVG0/arcgis/rest/services/weather_stations_010417/FeatureServer/0";
    
    var outFields = [ "ELEVATION", "STATION_NAME", "COUNTRY", "TEMP", "R_HUMIDITY", 
                     "WIND_SPEED", "WIND_CHILL", "HEAT_INDEX", "WEATHER" ];
    
    var popupTemplate = new PopupTemplate({
      title: "{STATION_NAME}, {COUNTRY}",
      fieldInfos: [{
        fieldName: "ELEVATION",
        label: "Elevation",
        visible: "true"
      }, {
        fieldName: "TEMP",
        label: "Temperature (°F)",
        visible: "true"
      }, {
        fieldName: "R_HUMIDITY",
        label: "Relative humidity",
        visible: "true"
      }, {
        fieldName: "WIND_SPEED",
        label: "Wind speed (km/hr)",
        visible: "true"
      }, {
        fieldName: "WIND_CHILL",
        label: "Wind chill",
        visible: "true"
      }, {
        fieldName: "HEAT_INDEX",
        label: "Heat index",
        visible: "true"
      }, {
        fieldName: "WEATHER",
        label: "Weather conditions",
        visible: "true"
      }]
    });
    
    var weatherStationLayer = new FeatureLayer(serviceUrl, {
      outFields: outFields, 
      infoTemplate: popupTemplate
    });
    
    var map = new Map("viewDiv", {
      basemap: "dark-gray",
      center: [ -95, 39 ],
      zoom: 3
    });
    
    map.addLayer(weatherStationLayer);
    
    var legend;
    map.on("load", function(evt){
      
      legend = new Legend({
        map: map,
        layerInfos: [{
          layer: weatherStationLayer,
          title: "Current weather conditions"
        }]
      }, "legendDiv");
      legend.startup();
      
      changeRenderer(document.getElementById("change-viz").value);
    });
    
    // Stores each arcade expression as a string variable 
    // to be used in renderers
    
    var windChillArcade = document.getElementById("wind-chill").text;
    var heatIndexArcade = document.getElementById("heat-index").text;
    var apparentTempArcade = document.getElementById("apparent-temp").text;
    var feltTempDiffArcade = document.getElementById("felt-temp-diff").text;
    var arrowDirectionArcade = document.getElementById("arrow-direction").text;
    var feltTempDiffAbsArcade = document.getElementById("felt-temp-diff-abs").text;
    
    // Renderers based on each of the Arcade expressions above
    
    // WIND CHILL
    
    var windChillRenderer = new SimpleRenderer({
      symbol: createSymbol(),
      label: "Weather station",
      visualVariables: [{
        type: "colorInfo",
        valueExpression: windChillArcade,
        valueExpressionTitle: "Wind chill (°F)",
        stops: [
          { value: 9, color: new Color("#f8f8db") },
          { value: 23, color: new Color("#82b1bf") },
          { value: 37, color: new Color("#425970") }
        ]
      }]
    });
    
    // HEAT INDEX

    var heatIndexRenderer = new SimpleRenderer({
      symbol: createSymbol(),
      label: "Weather station",
      visualVariables: [{
        type: "colorInfo",
        valueExpression: heatIndexArcade,
        valueExpressionTitle: "Heat index (°F)",
        stops: [
          { value: 84, color: new Color("#fdf4d2") },
          { value: 88, color: new Color("#df9465") },
          { value: 93, color: new Color("#873634") }
        ]
      }]
    });
    
    // APPARENT TEMPERATURE (WHAT IT ACTUALLY FEELS LIKE)

    var apparentTempRenderer = new SimpleRenderer({
      symbol: createSymbol(),
      label: "Weather station",
      visualVariables: [{
        type: "colorInfo",
        valueExpression: apparentTempArcade,
        valueExpressionTitle: "Apparent temperature (°F)",
        stops: [
          { value: -15, color: new Color("#00C5FF") },
          { value: 12, color: new Color("#BEE8FF") },
          { value: 32, color: new Color("#fdf4d2") },
          { value: 64, color: new Color("#FF7F7F") },
          { value: 87, color: new Color("#E60000") }
        ]
      }]
    });

    // CHANGE BETWEEN APPARENT TEMPERATURE AND ACTUAL MEASURED AIR TEMP
    
    var feltTempDiffRenderer = new SimpleRenderer({
      symbol: createSymbol(),
      label: "Weather station",
      visualVariables: [{
        type: "colorInfo",
        valueExpression: feltTempDiffArcade,
        valueExpressionTitle: "Difference between apparent temperature and measured air temperature",
        stops: [
          { value: -34, color: new Color("#0571b0"), label: "< -34 (colder than air temp)" },
          { value: -15, color: new Color("#0571b0"), label: "-15 (colder than air temp)" },
          { value: 0, color: new Color("#fdf4d2"), label: "0 (same as air temp)" },
          { value: 10, color: new Color("#E60000"), label: "10 (warmer than air temp)" },
          { value: 17, color: new Color("#E60000"), label: "> 17 (warmer than air temp)" }
        ]
      }]
    });
    
    // Multivariate visualization of 
    // CHANGE BETWEEN APPARENT TEMPERATURE AND ACTUAL MEASURED AIR TEMP
    // and whether the change was warmer or cooler
    
    var feltTempDiffRotationRenderer = new SimpleRenderer({
      symbol: createSymbol(true),
      label: "Weather station",
      visualVariables: [{
        type: "colorInfo",
        valueExpression: apparentTempArcade,
        valueExpressionTitle: "Apparent temperature (°F)",
        stops: [
          { value: -15, color: new Color("#00C5FF") },
          { value: 12, color: new Color("#BEE8FF") },
          { value: 32, color: new Color("#fdf4d2") },
          { value: 64, color: new Color("#FF7F7F") },
          { value: 87, color: new Color("#E60000") }
        ]
      }, {
        type: "sizeInfo",
        valueExpression: feltTempDiffAbsArcade,
        valueExpressionTitle: "Difference between apparent temperature and measured air temperature",
        minDataValue: 2,
        maxDataValue: 34,
        minSize: 6,
        maxSize: 50
      }, {
        type: "rotationInfo",
        valueExpression: arrowDirectionArcade,
      }]
    });
  
    // Map renderers to select options and basemaps
    
    var renderers = {
      "wind-chill": {
        renderer: windChillRenderer,
        basemap: "gray"
      },
      "heat-index": {
        renderer: heatIndexRenderer,
        basemap: "gray"
      },
      "felt-temp": {
        renderer: apparentTempRenderer,
        basemap: "dark-gray"
      },
      "felt-temp-diff": {
        renderer: feltTempDiffRenderer,
        basemap: "dark-gray"
      },
      "felt-temp-diff-rotation": {
        renderer: feltTempDiffRotationRenderer,
        basemap: "dark-gray"
      }
    };
    
    // executes each time the user selects a new 
    // variable to visualize
    
    function changeRenderer (variable){
      var renderer = renderers[variable].renderer;
      weatherStationLayer.setRenderer(renderer);
      
      var basemap = renderers[variable].basemap;
      if (basemap !== map.getBasemap()){
        map.setBasemap(basemap);
      }
      
      weatherStationLayer.redraw();
      legend.refresh();
    }
    
    // fires on each change of the select
    
    on(document.getElementById("change-viz"), "change", function(evt){
      changeRenderer(evt.target.value);
    });

    // helper function for creating symbols
    
    function createSymbol (isArrow){
      var outline = new SimpleLineSymbol()
        .setColor(new Color([ 92, 92, 92, 0.2 ]))
        .setWidth(0.5);
      
      var markerSymbol = new SimpleMarkerSymbol()
        .setColor(new Color([0,0,0,0]))
        .setSize(5)
        .setOutline(outline);
      
      if (isArrow){
        markerSymbol.setPath("M14.5,29 23.5,0 14.5,9 5.5,0z");
      }
      
      return markerSymbol;
    }
  });

</script>

</head>

<body class="claro">
  <div id="viewDiv"></div>
  <div id="info">
    <select id="change-viz">
      <option value="wind-chill">Wind chill</option>
      <option value="heat-index">Heat index</option>
      <option value="felt-temp" selected>Apparent temperature</option>
      <option value="felt-temp-diff">Difference between felt and measured temp</option>
      <option value="felt-temp-diff-rotation">Apparent temp w/ difference measured</option>
    </select>
    <div id="legendDiv"></div>
  </div>
</body>

</html>